//
//  LBHTTPSessionManager.m
//  LBNetworking
//
//  Created by WuShiHai on 29/03/2017.
//  Copyright © 2017 408939786@qq.com. All rights reserved.
//

#import "LBHTTPSessionManager.h"
#import "LBNetworkReachability.h"
#import <LBFoundation/LBFoundation.h>

#define WL_CONFIGURATION_RESPONSE_VALUE(key) self.configuration.response.key

@interface LBHTTPSessionManager ()

@property (nonatomic, strong) LBHTTPConfiguration *configuration;
@property (nonatomic, strong) NSString *baseURLString;
@property (nonatomic, strong) Class<LBHTTPSessionManagerProtocol> protocol;

@end

@implementation LBHTTPSessionManager

#pragma mark - 生成manager实例

+ (instancetype)manager:(NSString *)baseURLString
          configuration:(LBHTTPConfiguration *)configuration
               protocol:(Class<LBHTTPSessionManagerProtocol>)protocol{
    NSURL *baseURL = [LBHTTPSessionManager URLWithString:baseURLString];
    
    configuration = configuration?:[LBHTTPConfiguration defaultConfiguration];
    
    NSURLSessionConfiguration *wl_configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    wl_configuration.timeoutIntervalForRequest = configuration.timeoutInterval;

    LBHTTPSessionManager *manager = [[[self class] alloc] initWithBaseURL:baseURL sessionConfiguration:wl_configuration];
    manager.configuration = configuration;
    manager.baseURLString = baseURLString;
    manager.protocol = protocol;
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    [self fillHeaders:manager.requestSerializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json",@"text/plain", nil];
    
    return manager;
}

+ (instancetype)manager{
    LBHTTPSessionManager * manager = [super manager];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];
    [self fillHeaders:manager.requestSerializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json",@"text/plain", nil];
    return manager;
}

+ (void)fillHeaders:(AFHTTPRequestSerializer *)requestSerializer {
    NSDictionary *headers = @{
        @"x-app": @"blushmark-ios",
#if DEBUG || TESTRELEASE
        @"x-env": @"internal",
#else
        @"x-env": @"production",
#endif
        @"x-version": AppReleaseVersionNumber(),
        @"Authorization": @"Basic bGViYmF5OnBhc3N3MHJk",
        @"idfv": [LBDeviceUtil idfvString] ?: @"",
        @"idfa": [LBDeviceUtil idfaString] ?: @"",
    };
    [headers enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        [requestSerializer setValue:obj forHTTPHeaderField:key];
    }];
}

#pragma mark - 暴露的公共方法
- (NSURLSessionDataTask *)POST:(NSString *)URLString
                    parameters:(NSDictionary *)parameters
               completeHandler:(LBHTTPResponseBlock)completeHandler{
    return [self request:LBHTTPSessionMethodPOST
               URLString:URLString
              parameters:parameters
           configuration:self.configuration
         completeHandler:completeHandler];
}

- (NSURLSessionDataTask *)request:(LBHTTPSessionMethod)method
                        URLString:(NSString *)URLString
                       parameters:(NSDictionary *)parameters
                    configuration:(LBHTTPConfiguration *)configuration
                  completeHandler:(LBHTTPResponseBlock)completeHandler{
    
    if (configuration == nil) {
        configuration = self.configuration ?: [LBHTTPConfiguration defaultConfiguration];
    }
    if (configuration != self.configuration) {
        return [[LBHTTPSessionManager manager:[self.baseURL absoluteString]
                                configuration:configuration
                                     protocol:self.protocol] request:method
                                                           URLString:URLString
                                                          parameters:parameters
                                                       configuration:configuration
                                                     completeHandler:completeHandler];
    }
    
    if (method == LBHTTPSessionMethodPOST
        || method == LBHTTPSessionMethodGET
        || method == LBHTTPSessionMethodDELETE
        || method == LBHTTPSessionMethodPUT) {
                
        //应用层再封装处理
        if ([(Class)self.protocol respondsToSelector:@selector(packageParameters:)]) {
            parameters = [self.protocol packageParameters:parameters];
        }
        
        id logInfoMessage;
        if ([parameters isKindOfClass:[NSDictionary class]]) {
            logInfoMessage = [LBHTTPSessionManager dicToString:parameters];
        } else {
            logInfoMessage = parameters;
        }
        
        if (![LBNetworkReachability isReachable]) {//网络不可达
            [self parseResponseFailed:nil
                            URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString]
                           parameters:logInfoMessage
                             response:nil
                    completionHandler:completeHandler
                              logInfo:@"网络不可达"];
            return nil;
        }
        
        CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    
#define LBHTTPSessionManagerRequestFunc(METHOD) \
        __weak __typeof(&*self) weakSelf = self; \
        NSURLSessionDataTask *task = [self METHOD:URLString \
                                       parameters:parameters \
                                          success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) { \
                                              [weakSelf reqeustSuccess:task \
                                                            parameters:logInfoMessage \
                                                        responseObject:responseObject \
                                                             startTime:startTime \
                                                     completionHandler:completeHandler]; \
                                          } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { \
                                              [weakSelf reqeustFailed:task \
                                                           parameters:logInfoMessage \
                                                                error:error \
                                                            startTime:startTime \
                                                    completionHandler:completeHandler]; \
                                          }]; \
        //app 层回调 \
        if ([(Class)self.protocol respondsToSelector:@selector(request:)]) { \
            [self.protocol request:task]; \
        } \
         \
        DDLogInfo(@"\n接口地址1[%@]:%@\n请求参数:%@", task.currentRequest.URL.absoluteString, @"asfd", logInfoMessage); \
         \
        return task;
        
        if (method == LBHTTPSessionMethodPOST) {
            LBHTTPSessionManagerRequestFunc(POST);
        } else if (method == LBHTTPSessionMethodGET) {
            LBHTTPSessionManagerRequestFunc(GET);
        } else if (method == LBHTTPSessionMethodDELETE) {
            LBHTTPSessionManagerRequestFunc(DELETE);
        } else if (method == LBHTTPSessionMethodPUT) {
            LBHTTPSessionManagerRequestFunc(PUT);
        } else {
            return nil;
        }
#undef LBHTTPSessionManagerRequestFunc(METHOD)
    } else {
        NSAssert(1, @"暂未添加该LBHTTPSessionMethod");
        return nil;
    }
    return nil;
}

#pragma mark - Parse
- (void)reqeustSuccess:(NSURLSessionDataTask *)task
            parameters:(id)parameters
        responseObject:(id)responseObject
             startTime:(CFAbsoluteTime)startTime
     completionHandler:(LBHTTPResponseBlock)completionHandler{
    [self parseResponseSuccess:responseObject
                     URLString:task.currentRequest.URL.absoluteString
                    parameters:parameters
             completionHandler:completionHandler
                       logInfo:task.currentRequest.URL.absoluteString];
}

- (void)reqeustFailed:(NSURLSessionDataTask *)task
           parameters:(id)parameters
                error:(NSError *)error
            startTime:(CFAbsoluteTime)startTime
    completionHandler:(LBHTTPResponseBlock)completionHandler{
    [self parseResponseFailed:error
                    URLString:task.currentRequest.URL.absoluteString
                   parameters:parameters
                     response:nil
            completionHandler:completionHandler
                      logInfo:error.domain];
}

- (void)parseResponseSuccess:(id)responseObject
                   URLString:(NSString *)URLString
                  parameters:(id)parameters
           completionHandler:(LBHTTPResponseBlock)completionHandler
                     logInfo:(NSString *)info{
    
    LBHTTPResponse *response = [LBHTTPResponse new];
    response.parameters = parameters;
    response.response = responseObject;
    response.url = URLString;
    
    //数据不分成功失败均赋值
    id data = nil;
    if ([responseObject isKindOfClass:[NSDictionary class]] && WL_CONFIGURATION_RESPONSE_VALUE(dataKey)) {
        data = [responseObject objectForKey:WL_CONFIGURATION_RESPONSE_VALUE(dataKey)];
    }
    if ([data isKindOfClass:[NSString class]]) {
        id parseData = [LBHTTPSessionManager parseStringToArrayOrNSDictionary:data];
        if ([parseData isKindOfClass:[NSDictionary class]]) {
            response.responseDictionary = parseData;
        }else if ([parseData isKindOfClass:[NSArray class]]) {
            response.responseArray = parseData;
        }
    }if ([data isKindOfClass:[NSDictionary class]]) {
        response.responseDictionary = data;
    }else if ([data isKindOfClass:[NSArray class]]) {
        response.responseArray = data;
    }
    
    NSString *code = [NSString stringWithFormat:@"%@",[responseObject valueForKey:WL_CONFIGURATION_RESPONSE_VALUE(codeKey)]?:@""];
    BOOL isSuccess = NO;
    if ([(Class)self.protocol respondsToSelector:@selector(isSuccess:)]) {
        isSuccess = [self.protocol isSuccess:code];
    }else {
        isSuccess = [self.configuration.response isSuccess:code];
    }
    if (isSuccess) {//返回成功
        //app 层回调
        if ([(Class)self.protocol respondsToSelector:@selector(response:)]) {
            [self.protocol response:response];
        }
        completionHandler?completionHandler(nil,response):nil;//成功
        
        if ([responseObject isKindOfClass:[NSDictionary class]]) {
            DDLogInfo(@"\n接口地址:%@\n返回数据:%@\n请求参数:%@",info,[LBHTTPSessionManager dicToString:(NSDictionary *)responseObject], parameters);
        }else{
            DDLogInfo(@"\n接口地址:%@\n返回数据:%@\n请求参数:%@",info,(NSDictionary *)responseObject, parameters);
        }
        
    }else{
        [self parseResponseFailed:responseObject
                        URLString:URLString
                       parameters:parameters
                         response:response
                completionHandler:completionHandler
                          logInfo:info];
    }
}
//data 可能为NSError 或者NSDictionary
- (void)parseResponseFailed:(id)responseObject
                  URLString:(NSString *)URLString
                 parameters:(id)parameters
                   response:(LBHTTPResponse *)response
          completionHandler:(LBHTTPResponseBlock)completionHandler
                    logInfo:(NSString *)info{
    if (response == nil) {
        response = [LBHTTPResponse new];
        response.parameters = parameters;
        response.url = URLString;
    }
    
    if ([responseObject isKindOfClass:[NSDictionary class]]) {
        DDLogInfo(@"\n接口地址:%@\n返回数据:%@\n请求参数:%@",info,[LBHTTPSessionManager dicToString:(NSDictionary *)responseObject], parameters);
    }else{
        DDLogInfo(@"\n接口地址:%@\n返回数据:%@\n请求参数:%@",info,(NSDictionary *)responseObject, parameters);
    }
    
    response.response = responseObject;

    if (![LBNetworkReachability isReachable]) {//网络不可达
        response.errorCode = LBHTTPResponseErrorNetwork;
        response.errorMsg = @"网络异常";
        if ([responseObject isKindOfClass:[NSError class]]) {
            response.code = [(NSError *)responseObject code];
        }
    }else{
        //系统错误类的
        if ([responseObject isKindOfClass:[NSError class]]) {
            DDLogInfo(@"本地请求报错:%@:%@",info,responseObject);
            response.code = [(NSError *)responseObject code];
            response.errorMsg = @"服务器异常";
            response.errorCode = [LBHTTPResponse errorServerCode:response.code];
        }else{
            //服务器请求成功,但是操作失败,这个需要由外部定义成功码
            if ([responseObject isKindOfClass:[NSDictionary class]]) {
                id codeObject = [responseObject objectForKey:WL_CONFIGURATION_RESPONSE_VALUE(codeKey)];
                if ([codeObject isKindOfClass:[NSNumber class]] || [codeObject isKindOfClass:[NSString class]]) {
                    if ([codeObject isKindOfClass:[NSNumber class]]){
                        codeObject = [codeObject stringValue];
                    }
                    response.code = [codeObject integerValue];
                    response.errorMsg = [responseObject objectForKey:WL_CONFIGURATION_RESPONSE_VALUE(messageKey)];
                    response.errorCode = LBHTTPResponseErrorBusiness;
                }else{
                    response.errorCode = LBHTTPResponseErrorUnknow;
                    response.errorMsg = @"接口返回异常";
                    DDLogInfo(@"服务器返回字段异常,暂时无法解析");
                }
            }else{
                response.errorCode = LBHTTPResponseErrorUnknow;
                response.errorMsg = @"接口返回异常";
                DDLogInfo(@"服务器返回非字典的数据类型,暂时无法解析");
            }
        }
    }
    
    NSError *error = [response isKindOfClass:[NSError class]]?(NSError *)response:[[NSError alloc] initWithDomain:response.errorMsg?:@"" code:response.code userInfo:nil];
    
    //取消的不需要回调
    if (error.code != -999) {
        //app 层回调
        if ([(Class)self.protocol respondsToSelector:@selector(response:)]) {
            [self.protocol response:response];
        }
        completionHandler?completionHandler(error,response):nil;
    }
}

#pragma mark - Private
+ (NSString *)dicToString:(NSDictionary *)dic{
    if ([dic isKindOfClass:[NSDictionary class]] == NO) {
        return nil;
    }
    
    NSError* error = nil;
    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dic
                                                       options:0
                                                         error:&error];
    
    if (error != nil) {
        DDLogInfo(@"NSDictionary JSONString error: %@", [error localizedDescription]);
        return nil;
    } else {
        return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
}
+ (id)parseStringToArrayOrNSDictionary:(NSString *)string{
    if ([string isKindOfClass:[NSString class]] == NO) {
        return nil;
    }
    
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
    NSError *error = nil;
    id jsonObject = [NSJSONSerialization JSONObjectWithData:data
                                                    options:NSJSONReadingAllowFragments
                                                      error:&error];
    if (jsonObject != nil && error == nil){
        return jsonObject;
    }else{
        // 解析错误
        return nil;
    }
    
}

#pragma mark - Handle
+ (NSURL *)URLWithString:(NSString *)URLString{
    NSURL *baseURL = nil;
    if (URLString.length > 0) {
        baseURL = [[NSURL alloc] initWithString:URLString];
    }
    return baseURL;
}

@end
